package org.xtremeobjects.domain.states;

import org.apache.commons.scxml.io.SCXMLSerializer;
import org.apache.commons.scxml.model.SCXML;
import org.xtremeobjects.domain.states.State;
import org.xtremeobjects.domain.states.StateMachine;
import org.xtremeobjects.domain.states.annotated.StateMachineBuilderFromAnnotatedClass;
import org.xtremeobjects.domain.states.scxml.StateMachineToSCXML;
 
/**
 * @author    Juan
 */
public class StateMachineFactory {

	public static final StateMachineFactory Instance = 
		new StateMachineFactory();

	public StateMachine create(String name) { 
		return new StateMachine(name); 
		}

	public StateMachineRuntime createInstance(StateMachine definition) {
		return new StateMachineRuntime(definition);
	}
	
	public StateMachine createFromClass(Class<?> clazz) {
		StateMachineBuilderFromAnnotatedClass builder = new StateMachineBuilderFromAnnotatedClass();
		return builder.buildStateMachineForClass( clazz );
	}
	public StateMachine createSeminarTest() {
		StateMachine stateMachine = StateMachineFactory.Instance.create("Seminar Registration");

		// Enrollment
		State enrollement = stateMachine.state("Enrollement");

		// Propoused
		State proposed = enrollement.state("Proposed");
		proposed.from(stateMachine.start());

		//Scheduled
		State scheduled = enrollement.state("Scheduled");
		scheduled.from(proposed).event("scheduled");
		
		// OpenForEnrollement
		State openForEnrollement = enrollement.state("OpenForEnrollement");
		openForEnrollement.addActionForEntry("self.logSize()");
		openForEnrollement.from(scheduled).event("open");
		openForEnrollement.from(openForEnrollement)
			.guard("self.isSeatAvailable()", "seat available")
			.addAction("self.addStudent()")
			.event("student enrolled");
		
		// ClosedToEnrollement
		State closedToEnrollement = enrollement.state("ClosedToEnrollement");
		closedToEnrollement.addActionForEntry("self.notifyInstructor()");
		closedToEnrollement.from(openForEnrollement).event("closed");

		// Full
		State full = enrollement.state("Full");
		full.addActionForEvent("enroll student", "addToWaitingList()");
		full.addActionForEvent("enroll student", "considerSplit()");

		full.from(openForEnrollement)
			.guard("!self.isSeatAvailable()", "no seat available")
			.addAction("self.addToWaitingList()")
			.event("student enrolled");

		full.from(full)
			.guard("self.isSeatAvailable()", "seat available")
			.addAction("self.enrollFromWaitingList")
			.event("student dropped");
		
		full.from(full)
			.guard("!self.isSeatAvailable()", "no seat available")
			.event("student dropped");
		
		full.to(openForEnrollement)
			.event("seminar split");
		
		full.to(openForEnrollement)
			.event("seat available");
		
		full.to(closedToEnrollement)
			.event("closed");
		
		full.from(full)
			.event("student enrolled");
		
		//End
		State end = stateMachine.end();
		end.from(enrollement).event("cancelled");

		
		//BeingTaught
		State beingTaught = stateMachine.state("BeingTaught");
		
		beingTaught.from(closedToEnrollement)
			.event("term started");
		
		beingTaught.from(beingTaught)
			.guard("self.getSeminarSize()>0", "seminar is not empty")
			.event("student dropped");
		
		beingTaught.to(end)
			.guard("self.getSeminarSize()==0", "seminar is empty")
			.event("student dropped");
		
		State finalExams = stateMachine.state("FinalExams");
		finalExams.from(beingTaught)
			.event("classes end");
		
		finalExams.to(end)
			.event("closed");
		
		return stateMachine;
	}

	public StateMachineRuntime createInstanceForClass(Class<?> clazz) {
		return createInstance(createFromClass(clazz));
	}

	public String asSCXML( StateMachine sm ) {
		SCXML scxml = StateMachineToSCXML.createFrom( StateMachineFactory.Instance.createFromClass(Seminar.class));
		String text = SCXMLSerializer.serialize(scxml);
		return text;
	}

	public Object asSCXML(Class<?> clazz) {
		return asSCXML(createFromClass(clazz));
	}
}
